//
// Created by antler on 2018/12/6.
//

#include "Image.h"

#include <android/bitmap.h>

#include "LuaJavaHelper.h"

#include "GlobalMacros.h"

Image *Image::fromScreen(const Rect &rect) {
    jobject jBitmap = ImageUtils::fromScreen(rect);
    if (jBitmap) {
        Image *image = new(std::nothrow)Image();
        if (image) {
            image->initWith(jBitmap, IMAGE_FROM_TYPE_SCREEN);
            return image;
        }
    }
    return nullptr;
}

Image *Image::fromFile(const std::string &path) {
    jobject jBitmap = ImageUtils::fromFile(path);
    if (jBitmap) {
        Image *image = new(std::nothrow)Image();
        if (image) {
            image->initWith(jBitmap, IMAGE_FROM_TYPE_FILE);
            return image;
        }
    }
    return nullptr;
}

Image::Image()
        : mjBitmap(nullptr), mImageFromType(IMAGE_FROM_TYPE_KNOWN) {
    memset(&mBitmapInfo, 0, sizeof(mBitmapInfo));
}

Image::~Image() {
    LOGV("Image::~Image self:%p", this);
    release();
}

bool Image::initWith(jobject jBitmap, IMAGE_FROM_TYPE imageFromType) {
    setBitmapObject(jBitmap, imageFromType);
    return true;
}

void Image::release() {
    LOGV("Image::release self:%p", this);
    releaseBitmapObject();
}

bool Image::isOk() {
    return mjBitmap != nullptr;
}

Size Image::getSize() {
    if (!isOk()) {
        return Size::INVALID;
    }

    return Size(mBitmapInfo.width, mBitmapInfo.height);
}

int Image::getWidth() {
    return mBitmapInfo.width;
}

int Image::getHeight() {
    return mBitmapInfo.height;
}

void Image::setRotation(float degree) {
    do {
        if (!isOk()) {
            break;
        }

        jobject newjBitmap = ImageUtils::rotation(mjBitmap, degree);
        if (!newjBitmap) {
            break;
        }

        setBitmapObject(newjBitmap, mImageFromType);
    } while (0);
}

bool Image::saveToFile(const std::string &filePath, int quality) {
    do {
        if (!isOk()) {
            break;
        }

        if (filePath.empty()) {
            break;
        }

        return ImageUtils::saveToFile(mjBitmap, filePath, quality);
    } while (0);
    return false;
}

void Image::clip(const Rect &rect) {
    do {

        if (!isOk()) {
            break;
        }

        if (rect.isEmpty()) {
            break;
        }

        jobject newjBitmap = ImageUtils::clip(mjBitmap, rect);
        if (!newjBitmap) {
            break;
        }

        setBitmapObject(newjBitmap, mImageFromType);
    } while (0);
}

bool Image::getColor(const Point &pos, Color3B &c3b) {
    bool ret = false;

    void *addrPtr = lockPixels();
    if (addrPtr) {
        ret = getPixel(addrPtr, pos.x, pos.y, c3b);
    }
    unlockPixels();

    return ret;
}

Image *Image::binarize() {
    return nullptr;
}

jobject Image::getBitmapObject() {
    return mjBitmap;
}

void Image::setBitmapObject(jobject jBitmap, IMAGE_FROM_TYPE imageFromType) {
    releaseBitmapObject();

    JNIEnv *env = LuaJavaHelper::getInstance()->getEnv();
    if (!env) {
        return;
    }

    if (jBitmap) {
        mjBitmap = env->NewGlobalRef(jBitmap);
        AndroidBitmap_getInfo(env, mjBitmap, &mBitmapInfo);
        setImageFromType(imageFromType);
    }
}

void Image::releaseBitmapObject() {
    JNIEnv *env = LuaJavaHelper::getInstance()->getEnv();
    if (!env) {
        return;
    }

    if (isOk()) {
        env->DeleteGlobalRef(mjBitmap);
        mjBitmap = nullptr;
        memset(&mBitmapInfo, 0, sizeof(mBitmapInfo));
        setImageFromType(IMAGE_FROM_TYPE_KNOWN);
    }
}

std::string Image::getImageFromTypeName() {
    std::string imageFromTypeName;
    switch (mImageFromType) {
        case IMAGE_FROM_TYPE_SCREEN: {
            imageFromTypeName = "SCREEN";
            break;
        }
        case IMAGE_FROM_TYPE_FILE: {
            imageFromTypeName = "FILE";
            break;
        }
        default: {
            imageFromTypeName = "KNOWN";
            break;
        }
    }
    return imageFromTypeName;
}

void Image::setImageFromType(IMAGE_FROM_TYPE imageFromType) {
    mImageFromType = imageFromType;
}

void *Image::lockPixels() {
    void *addrPtr = nullptr;

    do {
        if (!isOk()) {
            break;
        }

        JNIEnv *env = LuaJavaHelper::getInstance()->getEnv();
        if (!env) {
            break;
        }

        AndroidBitmap_lockPixels(env, mjBitmap, &addrPtr);
    } while (0);

    return addrPtr;
}

void Image::unlockPixels() {
    do {
        if (!isOk()) {
            break;
        }

        JNIEnv *env = LuaJavaHelper::getInstance()->getEnv();
        if (!env) {
            break;
        }

        AndroidBitmap_unlockPixels(env, mjBitmap);
    } while (0);
}

bool Image::getPixel(void *addrPtr, int x, int y, Color3B &c3b) {
    bool ret = false;

    do {
        if (!addrPtr) {
            break;
        }

        if (!isOk()) {
            break;
        }

        if (x < 0 || x >= mBitmapInfo.width || y < 0 || y >= mBitmapInfo.height) {
            break;
        }

        char *pixel = (char *) (addrPtr) + y * mBitmapInfo.stride + (x << 2);
        c3b.r = pixel[0] & 0xFF;
        c3b.g = pixel[1] & 0xFF;
        c3b.b = pixel[2] & 0xFF;

        ret = true;
    } while (0);
    return ret;
}





